package com.cps.frameauth.service.impl;

import com.cps.frameauth.common.constants.JwtConstant;
import com.cps.framecommon.exception.BusinessException;
import com.cps.frameauth.model.dto.JwtBody;
import com.cps.frameauth.model.enums.TokenCodeEnum;
import com.cps.frameauth.service.TokenService;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;

import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * token工具类Service接口实现
 */
@Service
@Slf4j
public class TokenUtilServiceImpl implements TokenService {

    /**
     * token解析
     *
     * @param token
     * @return
     */
    @Override
    public JwtBody parseToken(String token) {
        Jws<Claims> claimsJws;
        try {
            claimsJws = Jwts.parser().setSigningKey(publicKeyFromBase64()).parseClaimsJws(token);
        } catch (JwtException e) {
            log.error("parseToken expired:{} error:{}", token, e.getMessage());
            if (e.getMessage().contains("expired")) {
                throw new BusinessException(
                        TokenCodeEnum.AUTH_TIME_OUT.getCode(), TokenCodeEnum.AUTH_TIME_OUT.getMessage());
            }
            throw new BusinessException(
                    TokenCodeEnum.AUTH_FAILED.getCode(), TokenCodeEnum.AUTH_FAILED.getMessage());
        }
        return new JwtBody(claimsJws.getBody());
    }

    /**
     * 公钥64位序列化
     *
     * @return PublicKey
     */
    private static PublicKey publicKeyFromBase64() {
        try {
            byte[] keyBytes = Base64Utils.decodeFromString(JwtConstant.PUBLIC_KEY);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(JwtConstant.ALGORITHM_FAMILY_NAME);
            return keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) {
            throw new IllegalArgumentException(ex);
        }
    }

    /**
     * 生成token
     *
     * @param data       需包含信息
     * @param secondTime 过期时间，默认三十分钟
     * @param subject    主键
     * @return
     */
    @Override
    public String createToken(String data, long secondTime, String subject) {
        JwtBuilder jwtBuilder = Jwts.builder();
        subject = subject != null ? subject : UUID.randomUUID().toString();
        Map<String, Object> map = new HashMap<>();
        if (!StringUtils.isEmpty(data)) {
            map.put("data", data);
        }
        jwtBuilder.setClaims(map).setSubject(subject);
        long currentTime = System.currentTimeMillis();
        if (secondTime == 0) {
            currentTime += 30 * 60 * 1000;
        } else {
            currentTime += secondTime * 1000;
        }
        Date newDate = new Date(currentTime);
        jwtBuilder.setExpiration(newDate);
        return jwtBuilder.signWith(SignatureAlgorithm.RS256, privateKeyFromBase64()).compact();
    }

    /**
     * 私钥64位序列化
     *
     * @return PrivateKey
     */
    private static PrivateKey privateKeyFromBase64() {
        try {
            byte[] keyBytes = Base64Utils.decodeFromString(JwtConstant.PRIVATE_KEY);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(JwtConstant.ALGORITHM_FAMILY_NAME);
            return keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) {
            throw new IllegalArgumentException(ex);
        }
    }
}
